home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / coherence / base.py < prev    next >
Text File  |  2009-05-12  |  23KB  |  631 lines

  1. # Licensed under the MIT license
  2. # http://opensource.org/licenses/mit-license.php
  3.  
  4. # Copyright 2006,2007,2008 Frank Scholz <coherence@beebits.net>
  5.  
  6. import string
  7. import socket
  8. import os, sys
  9. import traceback
  10. import copy
  11.  
  12. from twisted.python import filepath, util
  13. from twisted.internet import task, address, defer
  14. from twisted.internet import reactor
  15. from twisted.web import resource,static
  16.  
  17. import coherence.extern.louie as louie
  18.  
  19. from coherence import __version__
  20.  
  21. from coherence.upnp.core.ssdp import SSDPServer
  22. from coherence.upnp.core.msearch import MSearch
  23. from coherence.upnp.core.device import Device, RootDevice
  24. from coherence.upnp.core.utils import parse_xml, get_ip_address, get_host_address
  25.  
  26. from coherence.upnp.core.utils import Site
  27.  
  28. from coherence.upnp.devices.control_point import ControlPoint
  29. from coherence.upnp.devices.media_server import MediaServer
  30. from coherence.upnp.devices.media_renderer import MediaRenderer
  31. from coherence.upnp.devices.binary_light import BinaryLight
  32. from coherence.upnp.devices.dimmable_light import DimmableLight
  33.  
  34.  
  35. from coherence import log
  36.  
  37. class SimpleRoot(resource.Resource, log.Loggable):
  38.     addSlash = True
  39.     logCategory = 'coherence'
  40.  
  41.     def __init__(self, coherence):
  42.         resource.Resource.__init__(self)
  43.         self.coherence = coherence
  44.  
  45.     def getChild(self, name, request):
  46.         self.debug('SimpleRoot getChild %s, %s' % (name, request))
  47.         if name == 'oob':
  48.             """ we have an out-of-band request """
  49.             return static.File(self.coherence.dbus.pinboard[request.args['key'][0]])
  50.         try:
  51.             return self.coherence.children[name]
  52.         except:
  53.             return self
  54.  
  55.     def listchilds(self, uri):
  56.         self.info('listchilds %s' % uri)
  57.         if uri[-1] != '/':
  58.             uri += '/'
  59.         cl = ''
  60.         for c in self.coherence.children:
  61.             device = self.coherence.get_device_with_id(c)
  62.             if device != None:
  63.                 _,_,_,device_type,version = device.get_device_type().split(':')
  64.                 cl +=  '<li><a href=%s%s>%s:%s %s</a></li>' % (
  65.                                         uri,c,
  66.                                         device_type.encode('utf-8'), version.encode('utf-8'),
  67.                                         device.get_friendly_name().encode('utf-8'))
  68.  
  69.         for c in self.children:
  70.                 cl += '<li><a href=%s%s>%s</a></li>' % (uri,c,c)
  71.         return cl
  72.  
  73.     def render(self,request):
  74.         return """<html><head><title>Coherence</title></head><body>
  75. <a href="http://coherence.beebits.net">Coherence</a> - a Python DLNA/UPnP framework for the Digital Living<p>Hosting:<ul>%s</ul></p></body></html>""" % self.listchilds(request.uri)
  76.  
  77.  
  78. class WebServer(log.Loggable):
  79.     logCategory = 'webserver'
  80.  
  81.     def __init__(self, ui, port, coherence):
  82.         try:
  83.             if ui != 'yes':
  84.                 """ use this to jump out here if we do not want
  85.                     the web ui """
  86.                 raise ImportError
  87.  
  88.             self.warning("Web UI not supported atm, will return with version 0.7.0")
  89.             raise ImportError
  90.  
  91.  
  92.             from nevow import __version_info__, __version__
  93.             if __version_info__ <(0,9,17):
  94.                 self.warning( "Nevow version %s too old, disabling WebUI" % __version__)
  95.                 raise ImportError
  96.  
  97.             from nevow import appserver, inevow
  98.             from coherence.web.ui import Web, IWeb, WebUI
  99.             from twisted.python.components import registerAdapter
  100.  
  101.             def ResourceFactory( original):
  102.                 return WebUI( IWeb, original)
  103.  
  104.             registerAdapter(ResourceFactory, Web, inevow.IResource)
  105.  
  106.             self.web_root_resource = Web(coherence)
  107.             self.site = appserver.NevowSite( self.web_root_resource)
  108.         except ImportError:
  109.             self.site = Site(SimpleRoot(coherence))
  110.  
  111.         self.port = reactor.listenTCP( port, self.site)
  112.         coherence.web_server_port = self.port._realPortNumber
  113.         # XXX: is this the right way to do it?
  114.         self.warning( "WebServer on port %d ready" % coherence.web_server_port)
  115.  
  116.  
  117. class Plugins(log.Loggable):
  118.     logCategory = 'plugins'
  119.     _instance_ = None  # Singleton
  120.  
  121.     _valids = ("coherence.plugins.backend.media_server",
  122.                "coherence.plugins.backend.media_renderer",
  123.                "coherence.plugins.backend.binary_light",
  124.                "coherence.plugins.backend.dimmable_light")
  125.  
  126.     _plugins = {}
  127.  
  128.     def __new__(cls, *args, **kwargs):
  129.         obj = getattr(cls, '_instance_', None)
  130.         if obj is not None:
  131.             return obj
  132.         else:
  133.             obj = super(Plugins, cls).__new__(cls, *args, **kwargs)
  134.             cls._instance_ = obj
  135.             obj._collect(*args, **kwargs)
  136.             return obj
  137.  
  138.     def __repr__(self):
  139.         return str(self._plugins)
  140.  
  141.     def __init__(self, *args, **kwargs):
  142.         pass
  143.  
  144.     def __getitem__(self, key):
  145.         return self._plugins.__getitem__(key)
  146.  
  147.     def get(self, key,default=None):
  148.         try:
  149.             return self.__getitem__(key)
  150.         except KeyError:
  151.             return default
  152.  
  153.     def __setitem__(self, key, value):
  154.         self._plugins.__setitem__(key,value)
  155.  
  156.     def set(self, key,value):
  157.         return self.__getitem__(key,value)
  158.  
  159.     def keys(self):
  160.         return self._plugins.keys()
  161.  
  162.     def _collect(self, ids=_valids):
  163.         if not isinstance(ids, (list,tuple)):
  164.             ids = (ids)
  165.         try:
  166.             import pkg_resources
  167.             for id in ids:
  168.                 for entrypoint in pkg_resources.iter_entry_points(id):
  169.                     try:
  170.                         #print entrypoint, type(entrypoint)
  171.                         self._plugins[entrypoint.name] = entrypoint.load(require=False)
  172.                     except (ImportError, pkg_resources.ResolutionError), msg:
  173.                         self.warning("Can't load plugin %s (%s), maybe missing dependencies..." % (entrypoint.name,msg))
  174.                         self.info(traceback.format_exc())
  175.         except ImportError:
  176.             self.info("no pkg_resources, fallback to simple plugin handling")
  177.  
  178.         except Exception, msg:
  179.             self.warning(msg)
  180.  
  181.         if len(self._plugins) == 0:
  182.             self._collect_from_module()
  183.  
  184.     def _collect_from_module(self):
  185.         from coherence.extern.simple_plugin import Reception
  186.         reception = Reception(os.path.join(os.path.dirname(__file__),'backends'), log=self.warning)
  187.         self.info(reception.guestlist())
  188.         for cls in reception.guestlist():
  189.             self._plugins[cls.__name__.split('.')[-1]] = cls
  190.  
  191.  
  192. class Coherence(log.Loggable):
  193.     logCategory = 'coherence'
  194.     _instance_ = None  # Singleton
  195.  
  196.     def __new__(cls, *args, **kwargs):
  197.         obj = getattr(cls, '_instance_', None)
  198.         if obj is not None:
  199.             cls._incarnations_ += 1
  200.             return obj
  201.         else:
  202.             obj = super(Coherence, cls).__new__(cls)
  203.             cls._instance_ = obj
  204.             cls._incarnations_ = 1
  205.             obj.setup(*args, **kwargs)
  206.             obj.cls = cls
  207.             return obj
  208.  
  209.     def __init__(self, *args, **kwargs):
  210.         pass
  211.  
  212.     def clear(self):
  213.         """ we do need this to survive multiple calls
  214.             to Coherence during trial tests
  215.         """
  216.         self.cls._instance_ = None
  217.  
  218.     def setup(self, config={}):
  219.  
  220.         self.devices = []
  221.         self.children = {}
  222.         self._callbacks = {}
  223.         self.active_backends = {}
  224.  
  225.         self.dbus = None
  226.         self.config = config
  227.  
  228.         network_if = config.get('interface')
  229.  
  230.         self.web_server_port = int(config.get('serverport', 0))
  231.  
  232.         """ initializes logsystem
  233.             a COHERENCE_DEBUG environment variable overwrites
  234.             all level settings here
  235.         """
  236.  
  237.         try:
  238.             logmode = config.get('logging').get('level','warning')
  239.         except (KeyError,AttributeError):
  240.             logmode = config.get('logmode', 'warning')
  241.         _debug = []
  242.  
  243.         try:
  244.             subsystems = config.get('logging')['subsystem']
  245.             if isinstance(subsystems,dict):
  246.                 subsystems = [subsystems]
  247.             for subsystem in subsystems:
  248.                 try:
  249.                     if subsystem['active'] == 'no':
  250.                         continue
  251.                 except (KeyError,TypeError):
  252.                     pass
  253.                 self.info( "setting log-level for subsystem %s to %s" % (subsystem['name'],subsystem['level']))
  254.                 _debug.append('%s:%d' % (subsystem['name'].lower(), log.human2level(subsystem['level'])))
  255.         except (KeyError,TypeError):
  256.             subsystem_log = config.get('subsystem_log',{})
  257.             for subsystem,level in subsystem_log.items():
  258.                 #self.info( "setting log-level for subsystem %s to %s" % (subsystem,level))
  259.                 _debug.append('%s:%d' % (subsystem.lower(), log.human2level(level)))
  260.         if len(_debug) > 0:
  261.             _debug = ','.join(_debug)
  262.         else:
  263.             _debug = '*:%d' % log.human2level(logmode)
  264.         try:
  265.             logfile = config.get('logging').get('logfile',None)
  266.             if logfile != None:
  267.                 logfile = unicode(logfile)
  268.         except (KeyError,AttributeError,TypeError):
  269.             logfile = config.get('logfile', None)
  270.         log.init(logfile, _debug)
  271.  
  272.         self.louieplugin = louie.TwistedDispatchPlugin()
  273.         try:
  274.             louie.install_plugin(self.louieplugin)
  275.         except louie.error.PluginTypeError:
  276.             """ we do need this to survive multiple calls
  277.                 to Coherence during trial tests
  278.             """
  279.             pass
  280.  
  281.         self.warning("Coherence UPnP framework version %s starting..." % __version__)
  282.  
  283.         if network_if:
  284.             self.hostname = get_ip_address('%s' % network_if)
  285.         else:
  286.             try:
  287.                 self.hostname = socket.gethostbyname(socket.gethostname())
  288.             except socket.gaierror:
  289.                 self.warning("hostname can't be resolved, maybe a system misconfiguration?")
  290.                 self.hostname = '127.0.0.1'
  291.  
  292.         if self.hostname.startswith('127.'):
  293.             """ use interface detection via routing table as last resort """
  294.             def catch_result(hostname):
  295.                 self.hostname = hostname
  296.                 self.setup_part2()
  297.             d = defer.maybeDeferred(get_host_address)
  298.             d.addCallback(catch_result)
  299.         else:
  300.             self.setup_part2()
  301.  
  302.     def setup_part2(self):
  303.  
  304.         self.info('running on host: %s' % self.hostname)
  305.         if self.hostname.startswith('127.'):
  306.             self.warning('detection of own ip failed, using %s as own address, functionality will be limited', self.hostname)
  307.  
  308.         unittest = self.config.get('unittest', 'no')
  309.         if unittest == 'no':
  310.             unittest = False
  311.         else:
  312.             unittest = True
  313.  
  314.         self.ssdp_server = SSDPServer(test=unittest)
  315.         louie.connect( self.create_device, 'Coherence.UPnP.SSDP.new_device', louie.Any)
  316.         louie.connect( self.remove_device, 'Coherence.UPnP.SSDP.removed_device', louie.Any)
  317.         louie.connect( self.add_device, 'Coherence.UPnP.RootDevice.detection_completed', louie.Any)
  318.         #louie.connect( self.receiver, 'Coherence.UPnP.Service.detection_completed', louie.Any)
  319.  
  320.         self.ssdp_server.subscribe("new_device", self.add_device)
  321.         self.ssdp_server.subscribe("removed_device", self.remove_device)
  322.  
  323.         self.msearch = MSearch(self.ssdp_server,test=unittest)
  324.  
  325.         reactor.addSystemEventTrigger( 'before', 'shutdown', self.shutdown, force=True)
  326.  
  327.         self.web_server = WebServer( self.config.get('web-ui',None), self.web_server_port, self)
  328.  
  329.         self.urlbase = 'http://%s:%d/' % (self.hostname, self.web_server_port)
  330.  
  331.         #self.renew_service_subscription_loop = task.LoopingCall(self.check_devices)
  332.         #self.renew_service_subscription_loop.start(20.0, now=False)
  333.  
  334.         self.available_plugins = None
  335.  
  336.         self.ctrl = None
  337.  
  338.         try:
  339.             plugins = self.config['plugin']
  340.             if isinstance(plugins,dict):
  341.                 plugins=[plugins]
  342.         except:
  343.             plugins = None
  344.         if plugins is None:
  345.             plugins = self.config.get('plugins',None)
  346.  
  347.         if plugins is None:
  348.             self.info("No plugin defined!")
  349.         else:
  350.             if isinstance(plugins,dict):
  351.                 for plugin,arguments in plugins.items():
  352.                     try:
  353.                         if not isinstance(arguments, dict):
  354.                             arguments = {}
  355.                         self.add_plugin(plugin, **arguments)
  356.                     except Exception, msg:
  357.                         self.warning("Can't enable plugin, %s: %s!" % (plugin, msg))
  358.                         self.info(traceback.format_exc())
  359.             else:
  360.                 for plugin in plugins:
  361.                     try:
  362.                         if plugin['active'] == 'no':
  363.                             continue
  364.                     except (KeyError,TypeError):
  365.                         pass
  366.                     try:
  367.                         backend = plugin['backend']
  368.                         arguments = copy.copy(plugin)
  369.                         del arguments['backend']
  370.                         backend = self.add_plugin(backend, **arguments)
  371.                         if self.writeable_config() == True:
  372.                             if 'uuid' not in plugin:
  373.                                 plugin['uuid'] = str(backend.uuid)[5:]
  374.                                 self.config.save()
  375.                     except Exception, msg:
  376.                         self.warning("Can't enable plugin, %s: %s!" % (plugin, msg))
  377.                         self.info(traceback.format_exc())
  378.  
  379.         if self.config.get('controlpoint', 'no') == 'yes':
  380.             self.ctrl = ControlPoint(self)
  381.  
  382.         if self.config.get('use_dbus', 'no') == 'yes':
  383.             try:
  384.                 from coherence import dbus_service
  385.                 if self.ctrl == None:
  386.                     self.ctrl = ControlPoint(self)
  387.                 self.dbus = dbus_service.DBusPontoon(self.ctrl)
  388.             except Exception, msg:
  389.                 self.warning("Unable to activate dbus sub-system: %r" % msg)
  390.                 self.debug(traceback.format_exc())
  391.  
  392.     def add_plugin(self, plugin, **kwargs):
  393.         self.info("adding plugin %r", plugin)
  394.  
  395.         self.available_plugins = Plugins()
  396.  
  397.         try:
  398.             plugin_class = self.available_plugins.get(plugin,None)
  399.             if plugin_class == None:
  400.                 raise KeyError
  401.             for device in plugin_class.implements:
  402.                 try:
  403.                     device_class=globals().get(device,None)
  404.                     if device_class == None:
  405.                         raise KeyError
  406.                     self.info("Activating %s plugin as %s..." % (plugin, device))
  407.                     new_backend = device_class(self, plugin_class, **kwargs)
  408.                     self.active_backends[str(new_backend.uuid)] = new_backend
  409.                     return new_backend
  410.                 except KeyError:
  411.                     self.warning("Can't enable %s plugin, sub-system %s not found!" % (plugin, device))
  412.                 except Exception, msg:
  413.                     self.warning("Can't enable %s plugin for sub-system %s, %s!" % (plugin, device, msg))
  414.                     self.debug(traceback.format_exc())
  415.         except KeyError:
  416.             self.warning("Can't enable %s plugin, not found!" % plugin)
  417.         except Exception, msg:
  418.             self.warning("Can't enable %s plugin, %s!" % (plugin, msg))
  419.             self.debug(traceback.format_exc())
  420.  
  421.     def remove_plugin(self, plugin):
  422.         """ removes a backend from Coherence          """
  423.         """ plugin is the object return by add_plugin """
  424.         """ or an UUID string                         """
  425.  
  426.         if isinstance(plugin,basestring):
  427.             try:
  428.                 plugin = self.active_backends[plugin]
  429.             except KeyError:
  430.                 self.warning("no backend with the uuid %r found" % plugin)
  431.                 return ""
  432.  
  433.         try:
  434.             del self.active_backends[str(plugin.uuid)]
  435.             self.info("removing plugin %r", plugin)
  436.             plugin.unregister()
  437.             return plugin.uuid
  438.         except KeyError:
  439.             self.warning("no backend with the uuid %r found" % plugin.uuid)
  440.             return ""
  441.  
  442.     def writeable_config(self):
  443.         """ do we have a new-style config file """
  444.         from coherence.extern.simple_config import ConfigItem
  445.         if isinstance(self.config,ConfigItem):
  446.             return True
  447.         return False
  448.  
  449.     def store_plugin_config(self,uuid,items):
  450.         """ find the backend with uuid
  451.             and store in its the config
  452.             the key and value pair(s)
  453.         """
  454.         plugins = self.config.get('plugin')
  455.         if plugins is None:
  456.             self.warning("storing a plugin config option is only possible with the new config file format")
  457.             return
  458.         if isinstance(plugins,dict):
  459.             plugins = [plugins]
  460.         uuid = str(uuid)
  461.         if uuid.startswith('uuid:'):
  462.             uuid = uuid[5:]
  463.         if isinstance(items,tuple):
  464.             new = {}
  465.             new[items[0]] = items[1]
  466.         for plugin in plugins:
  467.             try:
  468.                 if plugin['uuid'] == uuid:
  469.                     for k,v in items.items():
  470.                         plugin[k] = v
  471.                     self.config.save()
  472.             except:
  473.                 pass
  474.         else:
  475.             self.info("storing plugin config option for %s failed, plugin not found" % uuid)
  476.  
  477.     def receiver( self, signal, *args, **kwargs):
  478.         #print "Coherence receiver called with", signal
  479.         #print kwargs
  480.         pass
  481.  
  482.     def shutdown( self,force=False):
  483.         if force == True:
  484.             self._incarnations_ = 1
  485.         if self._incarnations_ > 1:
  486.             self._incarnations_ -= 1
  487.             return
  488.         if self.louieplugin != None:
  489.             louie.remove_plugin(self.louieplugin)
  490.             self.louieplugin = None
  491.         for backend in self.active_backends.itervalues():
  492.             backend.unregister()
  493.         self.active_backends = {}
  494.         """ send service unsubscribe messages """
  495.         try:
  496.             if self.web_server.port != None:
  497.                 self.web_server.port.stopListening()
  498.                 self.web_server.port = None
  499.             if hasattr(self.msearch, 'double_discover_loop'):
  500.                 self.msearch.double_discover_loop.stop()
  501.             if hasattr(self.msearch, 'port'):
  502.                 self.msearch.port.stopListening()
  503.             if hasattr(self.ssdp_server, 'resend_notify_loop'):
  504.                 self.ssdp_server.resend_notify_loop.stop()
  505.             if hasattr(self.ssdp_server, 'port'):
  506.                 self.ssdp_server.port.stopListening()
  507.             #self.renew_service_subscription_loop.stop()
  508.         except:
  509.             pass
  510.         l = []
  511.         for root_device in self.get_devices():
  512.             for device in root_device.get_devices():
  513.                 d = device.unsubscribe_service_subscriptions()
  514.                 l.append(d)
  515.                 d.addCallback(device.remove)
  516.             d = root_device.unsubscribe_service_subscriptions()
  517.             l.append(d)
  518.             d.addCallback(root_device.remove)
  519.  
  520.         """anything left over"""
  521.         self.ssdp_server.shutdown()
  522.         dl = defer.DeferredList(l)
  523.         self.warning('Coherence UPnP framework shutdown')
  524.         return dl
  525.  
  526.     def check_devices(self):
  527.         """ iterate over devices and their embedded ones and renew subscriptions """
  528.         for root_device in self.get_devices():
  529.             root_device.renew_service_subscriptions()
  530.             for device in root_device.get_devices():
  531.                 device.renew_service_subscriptions()
  532.  
  533.     def subscribe(self, name, callback):
  534.         self._callbacks.setdefault(name,[]).append(callback)
  535.  
  536.     def unsubscribe(self, name, callback):
  537.         callbacks = self._callbacks.get(name,[])
  538.         if callback in callbacks:
  539.             callbacks.remove(callback)
  540.         self._callbacks[name] = callbacks
  541.  
  542.     def callback(self, name, *args):
  543.         for callback in self._callbacks.get(name,[]):
  544.             callback(*args)
  545.  
  546.     def get_device_by_host(self, host):
  547.         found = []
  548.         for device in self.devices:
  549.             if device.get_host() == host:
  550.                 found.append(device)
  551.         return found
  552.  
  553.     def get_device_with_usn(self, usn):
  554.         found = None
  555.         for device in self.devices:
  556.             if device.get_usn() == usn:
  557.                 found = device
  558.                 break
  559.         return found
  560.  
  561.     def get_device_with_id(self, device_id):
  562.         found = None
  563.         for device in self.devices:
  564.             id = device.get_id()
  565.             if device_id[:5] != 'uuid:':
  566.                 id = id[5:]
  567.             if id == device_id:
  568.                 found = device
  569.                 break
  570.         return found
  571.  
  572.     def get_devices(self):
  573.         return self.devices
  574.  
  575.     def get_local_devices(self):
  576.         return [d for d in self.devices if d.manifestation == 'local']
  577.  
  578.     def get_nonlocal_devices(self):
  579.         return [d for d in self.devices if d.manifestation == 'remote']
  580.  
  581.     def create_device(self, device_type, infos):
  582.         self.info("creating ", infos['ST'],infos['USN'])
  583.         if infos['ST'] == 'upnp:rootdevice':
  584.             self.info("creating upnp:rootdevice ", infos['USN'])
  585.             root = RootDevice(infos)
  586.         else:
  587.             self.info("creating device/service ",infos['USN'])
  588.             root_id = infos['USN'][:-len(infos['ST'])-2]
  589.             root = self.get_device_with_id(root_id)
  590.             device = Device(infos, root)
  591.         # fire this only after the device detection is fully completed
  592.         # and we are on the device level already, so we can work with them instead with the SSDP announce
  593.         #if infos['ST'] == 'upnp:rootdevice':
  594.         #    self.callback("new_device", infos['ST'], infos)
  595.  
  596.     def add_device(self, device):
  597.         self.info("adding device",device.get_usn())
  598.         self.devices.append(device)
  599.  
  600.     def remove_device(self, device_type, infos):
  601.         self.info("removed device",infos['ST'],infos['USN'])
  602.         device = self.get_device_with_usn(infos['USN'])
  603.         if device:
  604.             self.devices.remove(device)
  605.             device.remove()
  606.             if infos['ST'] == 'upnp:rootdevice':
  607.                 louie.send('Coherence.UPnP.Device.removed', None, usn=infos['USN'])
  608.                 louie.send('Coherence.UPnP.RootDevice.removed', None, usn=infos['USN'])
  609.                 self.callback("removed_device", infos['ST'], infos['USN'])
  610.  
  611.  
  612.     def add_web_resource(self, name, sub):
  613.         self.children[name] = sub
  614.  
  615.     def remove_web_resource(self, name):
  616.         try:
  617.             del self.children[name]
  618.         except KeyError:
  619.             """ probably the backend init failed """
  620.             pass
  621.  
  622.     def connect(self,receiver,signal=louie.signal.All,sender=louie.sender.Any, weak=True):
  623.         """ wrapper method around louie.connect
  624.         """
  625.         louie.connect(receiver,signal=signal,sender=sender,weak=weak)
  626.  
  627.     def disconnect(self,receiver,signal=louie.signal.All,sender=louie.sender.Any, weak=True):
  628.         """ wrapper method around louie.disconnect
  629.         """
  630.         louie.disconnect(receiver,signal=signal,sender=sender,weak=weak)
  631.